﻿using System;
using System.Linq;
using System.Threading.Tasks;
using Abp.Application.Services.Dto;
using FutureApp.Authorization.Roles;
using FutureApp.Roles.Dto;
using System.Collections.Generic;
using Abp.AutoMapper;
using Abp.Domain.Repositories;
using Abp.Authorization.Users;
using FutureApp.Users;
using FutureApp.Users.Dto;

namespace FutureApp.Roles
{
    /// <AppService/>
    public class RoleAppService : FutureAppAppServiceBase, IRoleAppService
    {
        private RoleManager RoleManager { get; }
        private IRepository<User, long> UserRepository { get; }
        private IRepository<UserRole, long> UserRoleRepository { get; }

        /// <constructor/>
        public RoleAppService(
            RoleManager roleManager,
            IRepository<User, long> userRepository,
            IRepository<UserRole, long> userRoleRepository)
        {
            this.RoleManager = roleManager;
            this.UserRepository = userRepository;
            this.UserRoleRepository = userRoleRepository;
        }

        /// <inheritDoc/>
        public async Task UpdateRolePermissions(UpdateRolePermissionsInputDto input)
        {
            var role = await this.RoleManager.GetRoleByIdAsync(input.RoleId);
            var grantedPermissions = this.PermissionManager
                .GetAllPermissions()
                .Where(p => input.GrantedPermissionNames.Contains(p.Name))
                .ToList();

            await this.RoleManager.SetGrantedPermissionsAsync(role, grantedPermissions);
        }

        /// <inheritDoc/>
        public async Task<GetRolePermissionsOutput> GetRolePermissions(GetRolePermissionsInput input)
        {
            var role = await this.RoleManager.GetRoleByIdAsync(input.RoleId);
            var allPermission = this.PermissionManager.GetAllPermissions();
            var grantedPermissions = await this.RoleManager.GetGrantedPermissionsAsync(input.RoleId);

            return new GetRolePermissionsOutput
            {
                Role = role.MapTo<RoleEntityDto>(),
                Permissions = new ListResultDto<PermissionEntityDto>(allPermission.MapTo<IReadOnlyList<PermissionEntityDto>>()),
                GrantedPermissions = new ListResultDto<PermissionEntityDto>(grantedPermissions.MapTo<IReadOnlyList<PermissionEntityDto>>())
            };
        }

        /// <inheritDoc/>
        public void UpdateRoleUsers(UpdateRoleUsersInputDto input)
        {
            this.UserRoleRepository.Delete(e => e.RoleId == input.RoleId);
            input.GrantedUserIds.ForEach(async uid => await this.UserRoleRepository.InsertAsync(
                new UserRole { RoleId = input.RoleId, UserId = uid, TenantId = this.AbpSession.TenantId }
            ));
        }

        /// <inheritDoc/>
        public async Task<GetRoleUsersOutput> GetRoleUsers(GetRoleUsersInput input)
        {
            var role = await this.RoleManager.GetRoleByIdAsync(input.RoleId);
            var grantedUser = from user in this.UserRepository.GetAll()
                              join userRole in this.UserRoleRepository.GetAll()
                              on user.Id equals userRole.UserId
                              where userRole.RoleId == input.RoleId
                              select user;
            var allUser = await this.UserRepository.GetAllListAsync();

            return new GetRoleUsersOutput
            {
                Role = role.MapTo<RoleEntityDto>(),
                Users = new ListResultDto<UserEntityDto>(allUser.MapTo<IReadOnlyList<UserEntityDto>>()),
                GrantedUsers = new ListResultDto<UserEntityDto>(grantedUser.MapTo<IReadOnlyList<UserEntityDto>>())
            };
        }

        /// <inheritDoc/>
        public ListResultDto<RoleEntityDto> GetRoles()
        {
            var roles = this.RoleManager.Roles.ToList();
            return new ListResultDto<RoleEntityDto>(roles.MapTo<List<RoleEntityDto>>());
        }

        /// <inheritDoc/>
        public async Task<RoleEntityDto> GetRoleById(int id)
        {
            var role = await this.RoleManager.GetRoleByIdAsync(id);
            return role.MapTo<RoleEntityDto>();
        }

        /// <inheritDoc/>
        public async Task CreateRole(CreateRoleInput input)
        {
            var role = input.MapTo<Role>();

            role.TenantId = this.AbpSession.TenantId;
            role.IsStatic = false;
            role.IsDefault = false;

            this.CheckErrors(await this.RoleManager.CreateAsync(role));
        }

        /// <inheritDoc/>
        public async Task UpdateRole(UpdateRoleInput input)
        {
            var role = await this.RoleManager.GetRoleByIdAsync(input.Id);
            role = input.MapTo(role);
            this.CheckErrors(await this.RoleManager.UpdateAsync(role));
        }

        /// <inheritDoc/>
        public async Task DeleteRole(int id)
        {
            var role = await this.RoleManager.GetRoleByIdAsync(id);
            this.CheckErrors(await this.RoleManager.DeleteAsync(role));
        }
    }
}